home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / master / Examples / TTXSame / DError.c < prev    next >
C/C++ Source or Header  |  1994-02-01  |  34KB  |  1,197 lines

  1. //***********************************************************************
  2. //*  Copyright (c) 1993, 1994 Obvious Implementation Corp.              *
  3. //*                           All Rights Reserved.                      *
  4. //*                     207 Livingstone Drive,                          *
  5. //*                     Cary N.C. 27513 - USA                           *
  6. //***********************************************************************
  7.  
  8. #include <stdio.h>
  9. #include <exec/types.h>
  10. #include <proto/exec.h>
  11. #include <proto/dos.h>
  12. #include <dos/dos.h>
  13. #include <dos/dosextens.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include <clib/exec_protos.h>
  17. #include <lib/rexx.h>
  18. #include <lib/misc.h>
  19.  
  20. #include "DError_rev.h"
  21.  
  22. #define PORTNAME "DICE_ERROR_PARSER"  // This is what everyone else calls us by
  23.  
  24. extern __stkargs LONG SetRexxVar(struct RexxMsg *,char *,char *,ULONG);
  25.  
  26. struct ErrorInfo {
  27.     struct ErrorInfo *next;      // Next error line in the list
  28.     struct ErrorInfo *prev;      // Previous line in the list
  29.     char             line[1];    // Text of the line.  Note the [1] for the NULL
  30. };
  31.  
  32. struct FileInfo {
  33.     struct FileInfo *next;       // Next file entry in the list
  34.     struct FileInfo *prev;       // Previous file entry in the list
  35.     char            *source;     // Source file that was compiled
  36.     char            *dir;        // Directory the file was compiled in
  37.     char            *args;       // Rexx arguments used for the compile
  38.     struct ErrorInfo base;       // Root node for all lines
  39.     struct ErrorInfo *cur;       // Current active error line
  40. };
  41.  
  42. struct FileInfo files;           // Root node for all files
  43. struct FileInfo *curfile;        // Current active error file
  44.  
  45. char *RexxHostName = NULL;       // We will create the host ourseleves
  46.  
  47. #define MAX_FILENAME 1024
  48. char buf[MAX_FILENAME+1];
  49. char ebuf[512];             // AREXX return string holding buffer
  50.  
  51. short run_arexx_server;
  52.  
  53. //***************************************************************************
  54. //* Procedure: AddLine                                                      *
  55. //* Synopsis:  AddLine(FileInfo *, char *)                                  *
  56. //* Purpose:   Adds the line to the current file information.  If there is  *
  57. //*            not enough memory, it will simply ignore the line silently.  *
  58. //***************************************************************************
  59. void AddLine(struct FileInfo *fi, char *line)
  60. {
  61.    struct ErrorInfo *ei;
  62.  
  63.    ei = malloc(sizeof(struct ErrorInfo) + strlen(line));
  64.    if (ei)
  65.    {
  66.       ei->prev = fi->base.prev;
  67.       ei->next = fi->base.prev->next;
  68.       strcpy(ei->line, line);
  69.       fi->base.prev->next = ei;
  70.       fi->base.prev = ei;
  71.    }
  72. }
  73.  
  74. //******************************************************************************
  75. //* Procedure: NewFile
  76. //* Synopsis:  FileInfo = NewFile(char *source, char *dir, char *args)
  77. //* Purpose:   Creates a new file info.  If it is unable to, it returns NULL
  78. //******************************************************************************
  79. struct FileInfo *NewFile(char *source, char *dir, char *args)
  80. {
  81.    struct FileInfo *fi;
  82.  
  83.    fi = malloc(sizeof(struct FileInfo));
  84.    if (fi)
  85.    {
  86.       fi->source = strdup(source);
  87.       fi->dir = strdup(dir);
  88.       fi->args = strdup(args);
  89.       fi->cur  = NULL;
  90.       if (fi->source != NULL &&
  91.           fi->dir    != NULL &&
  92.           fi->args   != NULL)
  93.       {
  94.           fi->prev = files.prev;
  95.           fi->next = files.prev->next;
  96.           files.prev->next = fi;
  97.           files.prev = fi;
  98.  
  99.           fi->base.prev = fi->base.next = &fi->base;
  100.       }
  101.       else
  102.       {
  103.          if (fi->source) free(fi->source);
  104.          if (fi->dir)    free(fi->dir);
  105.          if (fi->args)   free(fi->args);
  106.          free(fi);
  107.          fi = NULL;
  108.       }
  109.    }
  110.    return(fi);
  111. }
  112.  
  113. //************************************************************************
  114. //* Procedure: FreeFile
  115. //* Synopsis:  FreeFile(struct FileInfo *)
  116. //* Purpose:   Removes a FileInfo structure from the list
  117. //************************************************************************
  118. void FreeFile(struct FileInfo *fi)
  119. {
  120.    struct ErrorInfo *ei;
  121.  
  122.    //
  123.    // Handle any attempts to free the base FI
  124.    //
  125.    if (fi == &files) return;
  126.  
  127.    // Unlink us from the chain of file handles
  128.    fi->prev->next = fi->next;
  129.    fi->next->prev = fi->prev;
  130.  
  131.    //
  132.    // Update any system pointers which might look at what we are going to free
  133.    //
  134.    if (curfile == fi)
  135.    {
  136.       curfile = NULL;  // no more anyway
  137.    }
  138.  
  139.    //
  140.    // Free all the lines
  141.    //
  142.    fi->base.prev->next = NULL;  // mark our stopping point
  143.    for(ei = fi->base.next; ei != NULL;)
  144.    {
  145.       struct ErrorInfo *sei;
  146.  
  147.       sei = ei;
  148.       ei = ei->next;
  149.       free(sei);
  150.    }
  151.  
  152.    //
  153.    // Finally, get rid of the file information
  154.    //
  155.    free(fi->source);
  156.    free(fi->dir);
  157.    free(fi->args);
  158.    free(fi);
  159. }
  160.  
  161. //***********************************************************************
  162. //* Procedure: say
  163. //* Synopsis:  (void)say(msg);
  164. //* Purpose:   Displays the given message on the console
  165. //***********************************************************************
  166. void say(char *msg)
  167. {
  168.    BPTR out;
  169.    out = Output();
  170.    if (out)
  171.    {
  172.       Write(out, msg, strlen(msg));
  173.       Write(out, "\n", 1);
  174.    }
  175. }
  176.  
  177. //***********************************************************************
  178. //* Procedure: usage
  179. //* Synopsis:  (void)usage();
  180. //* Purpose:   Displays the command line usage message - does not return
  181. //***********************************************************************
  182. void usage(void)
  183. {
  184.    say("FILE/M,MACRO/K,PROJECT/K,REXXSTARTUP/S" VERSTAG);
  185.    exit(20);
  186. }
  187.  
  188. //***********************************************************************
  189. //* Procedure: dottx
  190. //* Synopsis:  result = dottx(port, cmd);
  191. //* Purpose:   Sends a command to TurboText
  192. //***********************************************************************
  193. char *dottx(char *port, char *cmd)
  194. {
  195.    char *res;
  196.    long ec;
  197.  
  198.    if (port == NULL) port = "TURBOTEXT";
  199.    PlaceRexxCommandDirect(NULL, port, cmd, &res, &ec);
  200.  
  201.    return(res);
  202. }
  203.  
  204. //***********************************************************************
  205. //* Procedure: full_path
  206. //* Synopsis:  path = full_path(name)
  207. //* Purpose:   Constructs a fully expanded filename
  208. //*            Note, we can not assume that the file exists, so it will
  209. //*            not be possible to actually lock it.  We can assume that
  210. //*            the directory it is part of does exist.  Note that it can
  211. //*            return NULL if there is no memory available.
  212. //***********************************************************************
  213. char *full_path(char *name)
  214. {
  215.    BPTR lock;
  216.    __aligned struct FileInfoBlock fib;
  217.    char *tail, *p;
  218.    int pos;
  219.  
  220.    //
  221.    // Step 1 - split out any directory information from the actual name
  222.    //
  223.    p = strrchr(name, '/');
  224.    if (p == NULL) p = strrchr(name, ':');
  225.    if (p != NULL)
  226.    {
  227.       //
  228.       // There was some directory information involved
  229.       //
  230.       char c;
  231.       tail = strdup(p+1);
  232.       c = p[1];
  233.       p[1] = 0;
  234.       lock = Lock(name, SHARED_LOCK);
  235.       p[1] = c;
  236.    }
  237.    else
  238.    {
  239.       //
  240.       // No directory information involved, just the name relative to the
  241.       // current directory
  242.       //
  243.       lock = Lock("", SHARED_LOCK);
  244.       tail = strdup(name);
  245.    }
  246.  
  247.    //
  248.    // Step 2 - we have the lock on the directory and the tail part of the name
  249.    // We want to construct a fully qualified path for the directory.
  250.    // If for some reason the lock on the directory returned 0, we want to just
  251.    // return the name they gave us to begin with.
  252.    //
  253.    if (lock == 0)
  254.    {
  255.       free(tail);
  256.       return(strdup(name));
  257.    }
  258.  
  259.    //
  260.    // Step 3 - Fully qualify the directory portion into the buffer
  261.    //
  262.    if (DOSBase->dl_lib.lib_Version >= 36)
  263.    {
  264.       if (!NameFromLock(lock, buf, MAX_FILENAME))
  265.       {
  266.          //
  267.          // Either the name is too long or there was something else wrong with
  268.          // the file name, just return what they gave us as a start
  269.          //
  270.          UnLock(lock);
  271.          free(tail);
  272.          return(strdup(name));
  273.       }
  274.       UnLock(lock);
  275.       pos = 0;
  276.    }
  277.    else
  278.    {
  279.       // Running under 1.3, we have to do this the old fashion way
  280.  
  281.       //
  282.       // Just so we don't have to do any inserts/extra copies, we will work
  283.       // from the end of the buffer and insert as we go
  284.       //
  285.       pos = MAX_FILENAME-1;  // Leave room for a '/' on the end sometimes
  286.       buf[--pos] = 0;
  287.       while(lock != 0)
  288.       {
  289.          BPTR parent;
  290.          int len;
  291.  
  292.          //
  293.          // Examine the lock to get the name for it
  294.          //
  295.          Examine(lock, &fib);
  296.  
  297.          //
  298.          // Find the parent of this directory
  299.          //
  300.          parent = ParentDir(lock);
  301.          UnLock(lock);
  302.          lock = parent;
  303.  
  304.          len = strlen(fib.fib_FileName);
  305.          pos -= 1;
  306.  
  307.          if (len > pos)
  308.          {
  309.             //
  310.             // oops, not enough room, just return the name they gave us
  311.             //
  312.             UnLock(lock);
  313.             free(tail);
  314.             return(strdup(name));
  315.          }
  316.          buf[pos] = lock ? ':' : '/';
  317.          pos -= len;
  318.          memcpy(buf+pos, fib.fib_FileName, len);
  319.       }
  320.    }
  321.  
  322.    //
  323.    // We have the path part in the buffer and the name part in the tail
  324.    // All that is left is to concatenate them together correctly
  325.    //
  326.    {
  327.       int len;
  328.  
  329.       //
  330.       // Successful, the buf holds the path for the directory.  We will need
  331.       // to add a / to the end if it doesn't end in a colon
  332.       //
  333.       len = strlen(buf+pos);
  334.       if ((buf[pos+len-1] != ':') && (buf[pos+len-1] != '/'))
  335.       {
  336.          buf[pos+len++] = '/';
  337.          buf[pos+len] = 0;
  338.       }
  339.       name = malloc(len+strlen(tail)+1);
  340.       if (name != NULL)
  341.       {
  342.          strcpy(name, buf+pos);
  343.          strcpy(name+len, tail);
  344.       }
  345.    }
  346.    return(name);
  347. }
  348.  
  349.  
  350. //***********************************************************************
  351. //* Procedure: SetStem
  352. //* Synopsis:  rc = SetStem(msg, stemstr)
  353. //* Purpose:   Set the appropriate AREXX stem variables based on the
  354. //*            current error message
  355. //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  356. //*
  357. //*  This sets the appropriate AREXX stem variables for the return
  358. //*     FILE:   The name of the file to edit
  359. //*     DIR:    The directory that the file is relative to
  360. //*     LINE:   The line number of the file to go to
  361. //*     ARGS:   The REXX arguments associated with the compile command
  362. //*     COL:    The column number in the file
  363. //*     ERRNO:  The error number
  364. //*     STRING: The error message to be printed out
  365. //*     TEXT:   The complete text of the original line
  366. //*     FPATH:  The full pathname of the file
  367. //*
  368. //***********************************************************************
  369. int SetStem(void *rxmsg, char *stem)
  370. {
  371.    char stembuf[40];
  372.    char *p;
  373.    char *str, *t;
  374.  
  375.    strncpy(stembuf, stem, 32);
  376.    stembuf[32] = 0;
  377.    p = stembuf+strlen(stembuf);
  378.  
  379.    if (curfile == NULL)      return(5);
  380.    if (curfile->cur == NULL) return(5);
  381.  
  382.    strcpy(p, ".DIR");
  383.    SetRexxVar(rxmsg, stembuf, curfile->dir, strlen(curfile->dir));
  384.  
  385.    strcpy(p, ".ARGS");
  386.    SetRexxVar(rxmsg, stembuf, curfile->args, strlen(curfile->args));
  387.  
  388.    str = curfile->cur->line;   // DC1: "fails.c" L:1 C:1 W:68 expected semicolon
  389.  
  390.    strcpy(p, ".TEXT");
  391.    SetRexxVar(rxmsg, stembuf, str, strlen(str));
  392.  
  393.    str = strchr(str, '\"');      // Locate the initial quote for the filename
  394.    if (str == NULL) return(5);   // Skip out if we don't find it.
  395.    str++;                        // Skip over the initial quote
  396.    t = strchr(str, '\"');        // Now look for the terminting quote
  397.    if (t == NULL) return(5);     // and skip out if it is not there
  398.    strcpy(p, ".FILE");
  399.    SetRexxVar(rxmsg, stembuf, str, t-str);
  400.  
  401.    //
  402.    // Get the full file name also.  For this we need to set the current
  403.    // directory and then use the fullpath routine
  404.    //
  405.    chdir(curfile->dir);
  406.    {
  407.       char c;
  408.       char *path;
  409.  
  410.       c = *t;                    // Remember the char that terminated the name
  411.       *t = '\0';                 // and replace it with a null
  412.       path = full_path(str);     // so that we can get the full path of the file
  413.       *t = c;                    // Put the original character back
  414.       strcpy(p, ".FPATH");
  415.       if (path)
  416.       {
  417.          SetRexxVar(rxmsg, stembuf, path, strlen(path));
  418.          free(path);
  419.       }
  420.       else
  421.       {
  422.          //
  423.          // We couldn't get a full path, just punt and use what we started with
  424.          //
  425.          SetRexxVar(rxmsg, stembuf, str, t-str);
  426.       }
  427.    }
  428.  
  429.    str = t + 1;                  // This should either be a NULL or the space
  430.  
  431.    //
  432.    // Parse out the L:<number> to get the line number
  433.    //
  434.    while (*str == ' ') str++;
  435.    t = str;
  436.    if (str[0] == 'L' && str[1] == ':')
  437.    {
  438.       str += 2;                  // Skip over the 'L:'
  439.       t = str;
  440.       while(*t >= '0' && *t <= '9') t++;
  441.    }
  442.    strcpy(p, ".LINE");
  443.    SetRexxVar(rxmsg, stembuf, str, t-str);
  444.    str = t;
  445.  
  446.    //
  447.    // Parse out the C:<number> to get the column number
  448.    //
  449.    while (*str == ' ') str++;
  450.    t = str;
  451.    if (str[0] == 'C' && str[1] == ':')
  452.    {
  453.       str += 2;                  // Skip over the 'C:'
  454.       t = str;
  455.       while(*t >= '0' && *t <= '9') t++;
  456.    }
  457.    strcpy(p, ".COL");
  458.    SetRexxVar(rxmsg, stembuf, str, t-str);
  459.    str = t;
  460.  
  461.    //
  462.    // Save away the error message to be displayed
  463.    //
  464.    while (*str == ' ') str++;
  465.    strcpy(p, ".STRING");
  466.    SetRexxVar(rxmsg, stembuf, str, strlen(str));
  467.  
  468.    //
  469.    // Lastly look for the remaining colon to get the error number
  470.    //
  471.    while(*str && *str != ':') str++;
  472.  
  473.    t = str;
  474.    if (str[0] == ':')
  475.    {
  476.       str++;
  477.       t = str;
  478.       while(*t >= '0' && *t <= '9') t++;
  479.  
  480.    }
  481.    strcpy(p, ".ERRNO");
  482.    SetRexxVar(rxmsg, stembuf, str, t-str);
  483.  
  484.    return(0);
  485. }
  486.  
  487. //***********************************************************************
  488. //* Procedure: do_next
  489. //* Synopsis:  do_next()
  490. //* Purpose:   Advance the current pointers to the next entry
  491. //*            If nothing is active, we wrap around to the beginning again
  492. //***********************************************************************
  493. void do_next(void)
  494. {
  495.  
  496.    if (curfile != NULL &&                    // Do we have any file active?
  497.        curfile->cur->next != &curfile->base) // With at least one more line?
  498.    {
  499.        //
  500.        // Normal case, Everything is fine, just advance to the next line
  501.        //
  502.        curfile->cur = curfile->cur->next;
  503.    }
  504.    else
  505.    {
  506.       //
  507.       // If there is no line active, go to the first one
  508.       // This can happen when they go past the end of the error messages
  509.       // or when they start out for the first time
  510.       //
  511.       if (curfile == NULL)
  512.          curfile = &files;
  513.  
  514.       //
  515.       // Now advance to the next file
  516.       //
  517.       curfile = curfile->next;
  518.  
  519.       if (curfile == &files)   // But make sure we actually have one
  520.       {
  521.          curfile = NULL;       // oops, no files at all, let them know
  522.       }
  523.       else
  524.       {
  525.          //
  526.          // Mark the first line as active.  Note that we can assume
  527.          // there there is always at least one line in a given file
  528.          // because of the way that do_load() works.
  529.          //
  530.          curfile->cur = curfile->base.next;
  531.       }
  532.    }
  533. }
  534.  
  535. //***********************************************************************
  536. //* Procedure: do_prev
  537. //* Synopsis:  do_prev()
  538. //* Purpose:   Advance the current pointers to the previous entry.
  539. //*            If we are at the beginning, don't do anything.
  540. //***********************************************************************
  541. void do_prev(void)
  542. {
  543.    //
  544.    // If there is no line active, go to the first one
  545.    // This can happen when they go past the end of the error messages
  546.    // or when they start out for the first time
  547.    //
  548.    if (curfile)        // Do we have any line active?
  549.    {
  550.       //
  551.       // Make sure we actually have a prev in the current source file
  552.       //
  553.       if (curfile->cur->prev == &curfile->base)
  554.       {
  555.          //
  556.          // No, we need to advance to the next file
  557.          //
  558.          curfile = curfile->prev;
  559.          if (curfile == &files)  // OOps, are we out of files?
  560.          {
  561.             curfile = NULL;
  562.          }
  563.          else
  564.          {
  565.             curfile->cur = curfile->base.prev;
  566.          }
  567.       }
  568.       else
  569.       {
  570.          //
  571.          // Everything is fine, just backup to the prev line
  572.          //
  573.          curfile->cur = curfile->cur->prev;
  574.       }
  575.    }
  576. }
  577.  
  578. //************************************************************************
  579. //* Procedure: do_load
  580. //* Synopsis:  lines = do_load(file, dir, source, args);
  581. //* Purpose:   Load a file into the error parsing.  A string constaining
  582. //*            the lines whcih corrspond to the source is returned.  This
  583. //*            string is not to be freed.
  584. //***********************************************************************
  585. char *do_load(char *efile, char *dir, char *source, char *args)
  586. {
  587.    char *result_append;
  588.    FILE *fh;
  589.    struct FileInfo *fi;
  590.    int result_space;
  591.  
  592.    fh = fopen(efile, "r");
  593.    if (fh == NULL)
  594.       return(NULL);
  595.  
  596.    result_append = ebuf;
  597.    result_space = sizeof(ebuf)-2;
  598.  
  599.    // See if the file is already in our list of things that are active
  600.    for (fi = files.next; fi != &files; fi = fi->next)
  601.    {
  602.       if (!stricmp(fi->source, source) &&
  603.           !stricmp(fi->dir,    dir))
  604.       {
  605.           // We found the existing file.  Just get rid of it
  606.           FreeFile(fi);
  607.           break;
  608.       }
  609.    }
  610.  
  611.    fi = NULL;
  612.    while(fgets(buf, MAX_FILENAME, fh) != NULL)
  613.    {
  614.       int len;
  615.       char *p, *line_text, *file_text;
  616.  
  617.       len = strlen(buf);
  618.       if (len && (buf[len-1] == '\n'))
  619.          buf[len-1] = '\0';
  620.  
  621.       //
  622.       // We need to do some sanity checking here on the line to make sure
  623.       // that it is really an error.  If not, we should just skip the line
  624.       // and go to the next one.  We only have to do a walk through on the
  625.       // line to see that it conforms to some basic sanity.  We will assume
  626.       // That pathological cases don't have to be parsed here because the
  627.       // SetStem routine does more rigerous parsing.
  628.       //
  629.  
  630.       // DC1: "fails.c" L:1 C:1 W:68 expected semicolon
  631.       if ((file_text = strchr(buf, '"'))  && // Find the first quote
  632.           (p = strchr(file_text+1, '"'))  && // And the closing quote
  633.           (line_text = strchr(p,   'L'))  && // Note, we are remembering the L: place
  634.           (line_text[1] == ':')           && // It should be followed by a :
  635.           (p = strchr(line_text+2, ':'))  && // Look for the : in C:
  636.           (p = strchr(p+1, ':')) )           // Look for the : after the error type
  637.       {
  638.          if (fi == NULL)
  639.          {
  640.             // Add the source file to the list of files
  641.             fi = NewFile(source, dir, args);
  642.  
  643.             // Of course if we don't have memory we want to leave gracefully
  644.             if (fi == NULL) return(NULL);
  645.  
  646.             curfile = fi;  // Set it up so we start at the current file
  647.          }
  648.  
  649.          //
  650.          // See if we also need to squirrel away the error line number
  651.          // for the result string.  We need to do this in the case where
  652.          // the source file of this message is the one that we are loading for
  653.          //
  654.          if (!memcmp(file_text+1, source, strlen(source)))
  655.          {
  656.             // Yes, it appears to be what we are looking for.
  657.             line_text += 2;                 // Skip over the L:
  658.             while (result_space &&          // Make sure there is room in the buffer
  659.                    (*line_text >= '0') &&
  660.                    (*line_text <= '9'))     // and copy the numbers which follow it
  661.             {
  662.                *result_append++ = *line_text++;
  663.                result_space--;
  664.             }
  665.             if (result_space)
  666.             {
  667.                *result_append++ = ' ';
  668.                result_space--;
  669.             }
  670.          }
  671.  
  672.          AddLine(fi, buf);
  673.       }
  674.    }
  675.  
  676.    *result_append = 0;    // Null terminate the result
  677.    if (curfile != NULL)
  678.    {
  679.       //
  680.       // Mark the first line as active.  Note that we can assume
  681.       // there there is always at least one line in a given file
  682.       // because of the way that do_load() works.
  683.       //
  684.       curfile->cur = curfile->base.next;
  685.    }
  686.    fclose(fh);
  687.    return(ebuf);
  688. }
  689.  
  690.  
  691. //***********************************************************************
  692. //* Procedure: do_ttx
  693. //* Synopsis:  do_ttx(file, macro, project)
  694. //* Purpose:   Invoke turbotext on a given file
  695. //*
  696. //***********************************************************************
  697. char *do_ttx(char *file, char *project, char *macro)
  698. {
  699.    char *document_list, *portname;
  700.  
  701.    portname = NULL;
  702.  
  703.    //
  704.    // Make sure that TurboText is actually running
  705.    //
  706.    {
  707. #define FIND_ITERATIONS 5
  708.       short Port_Find;
  709.  
  710.       for(Port_Find = FIND_ITERATIONS; Port_Find > 0; Port_Find--)
  711.       {
  712.           if (FindPort("TURBOTEXT") != NULL) break;
  713.           if (Port_Find == FIND_ITERATIONS)  // First time through
  714.              Execute("Turbotext:TTX BACKGROUND NOWINDOW", 0L, 0L);
  715.       Delay(50);
  716.       }
  717.       //
  718.       // Make sure that we were acutally able to start turbotext before
  719.       // going forward
  720.       //
  721.       if (Port_Find == 0)
  722.          return(NULL);
  723.    }
  724.  
  725.    //
  726.    // Send the command off to rexx to be processed
  727.    // We will wait here until it is complete
  728.    document_list = dottx(NULL, "GETDOCUMENTS");
  729.    if (document_list != NULL)
  730.    {
  731.       char *fname;
  732.  
  733.       //
  734.       // Now we need to go through and figure out all the files that are there
  735.       // Turbotext will return us a string in the form
  736.       //   "file1" TURBOTEXT1 "file2" TURBOTEXT2
  737.       // Unfortunately there are some pathological cases (such as having filenames
  738.       // with quotes in them and files having the string TURBOTEXT in them which
  739.       // make it hard to parse 100% accurately.
  740.       //
  741.       for(fname = document_list; *fname;)
  742.       {
  743.          char *file_path, *t;
  744.          int length;
  745.  
  746.          //
  747.          // We expect the string to be well formed.  Fall out on any
  748.          // pathological cases.
  749.          //
  750.          if (*fname != '"')
  751.          {
  752.             say("TURBOTEXT Sync Error");
  753.             free(document_list);
  754.             return("");
  755.          }
  756.          fname++;
  757.          t = fname;
  758.          length = strlen(t);
  759.          while ((length > 11) && memcmp(t, "\" TURBOTEXT", 11))
  760.          {
  761.             char *quote_pos;
  762.  
  763.             //
  764.             // We need to find the Quote which is immediately before
  765.             // the TURBOTEXT string
  766.             //
  767.             quote_pos = strchr(t+1, '"');
  768.             if (quote_pos == NULL)
  769.                break;
  770.             length -= (quote_pos-t);
  771.             t = quote_pos;
  772.          }
  773.  
  774.          //
  775.          // At this point, t should be pointing at the " in the name
  776.          //
  777.          *t = 0;
  778.  
  779.          //
  780.          // Now we want to get the portname for the file
  781.          //
  782.          t += 2;                 // Skip the '" '
  783.          portname = t;
  784.          while(*t && (*t != ' ')) t++;
  785.          if (*t) *t++ = 0;       // Null terminate it if necessary
  786.  
  787.          file_path = dottx(portname, "GetFilePath");
  788.  
  789.          //
  790.          // If this is the file that we are interested in, drop out of the
  791.          // loop and go to work on it in the file
  792.          //
  793.          if ((file_path != NULL) && !stricmp(file, file_path))
  794.          {
  795.             free(file_path);
  796.             portname = strdup(portname);
  797.             break;
  798.          }
  799.  
  800.          if (file_path != NULL) free(file_path);
  801.  
  802.          //
  803.          // Advance to let the next stuff work
  804.          //
  805.          fname = t;
  806.          while(*fname == ' ') fname++;
  807.          portname = NULL;
  808.       }
  809.       free(document_list);
  810.    }
  811.  
  812.  
  813.    //
  814.    // If we got a match, the port will tell us who to talk to
  815.    //
  816.    if (portname == NULL)
  817.    {
  818.       //
  819.       // No match here, we need to open up the file
  820.       //
  821.       char *open_cmd;
  822.  
  823.       open_cmd = malloc(strlen(file) + 15);  // strlen("OPENDOC NAME ")
  824.       if (open_cmd)
  825.       {
  826.          strcpy(open_cmd, "OPENDOC NAME ");
  827.          strcat(open_cmd, file);
  828.          portname = dottx(NULL, open_cmd);
  829.          free(open_cmd);
  830.       }
  831.    }
  832.    else
  833.    {
  834.       //
  835.       // The file is already open, we just need to bring it forward
  836.       //
  837.       char *pr;
  838.       pr = dottx(portname, "WINDOW2FRONT");
  839.       if (pr != NULL) free(pr);
  840.       pr = dottx(portname, "SCREEN2FRONT");
  841.       if (pr != NULL) free(pr);
  842.    }
  843.  
  844.    if (portname != NULL)
  845.    {
  846.       char *p;
  847.  
  848.       p = dottx(portname, "ACTIVATEWINDOW");
  849.       if (p != NULL) free(p);
  850.  
  851.       //
  852.       // We now have the document open, portname is the port to talk to
  853.       //
  854.       if (project != NULL)
  855.       {
  856.          char *call_cmd;
  857.          //
  858.          // ExecARexxString call setclip(TTX_TURBOTEXT12,myport.1)
  859.          // 000000000111111111122222222223333           3        3
  860.          // 123456789012345678901234567890123           4        5
  861.          //
  862.          call_cmd = malloc(strlen(project)+strlen(portname)+36);
  863.          if (call_cmd)
  864.          {
  865.             strcpy(call_cmd, "ExecARexxString call setclip(TTX_");
  866.             strcat(call_cmd, portname);
  867.             strcat(call_cmd, ",");
  868.             strcat(call_cmd, project);
  869.             strcat(call_cmd, ")");
  870.             p = dottx(portname, call_cmd);
  871.             if (p != NULL) free(p);
  872.             free(call_cmd);
  873.          }
  874.       }
  875.  
  876.       //
  877.       // Next we want to issue the macro command to make it run
  878.       //
  879.       if (macro != NULL)
  880.       {
  881.          char *p;
  882.          p = dottx(portname, macro);
  883.          if (p != NULL) free(p);
  884.       }
  885.    }
  886.  
  887.    //
  888.    // Since this will be returned to rexx, we need to ensure that this is
  889.    // somewhat permanent storage.  Unfortunately, we don't have a way to
  890.    // free the memory easily (we lose our context once the message has been
  891.    // sent back to arexx)
  892.    //
  893.    if (portname)
  894.    {
  895.       strcpy(ebuf, portname);
  896.       free(portname);
  897.       portname = ebuf;
  898.    }
  899.  
  900.    return(portname);
  901. }
  902.  
  903. //***********************************************************************
  904. //* Procedure: DoRexxCommand
  905. //* Synopsis:  DoRexxCommand(msg, port, arg, &result)
  906. //* Purpose:   Handle a incomming AREXX command
  907. //*
  908. //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  909. //*
  910. //*  We handle the following Rexx Commands:
  911. //*     QUIT
  912. //*     NEXT    <stem>
  913. //*     FIRST   <stem>
  914. //*     PREV    <stem>
  915. //*     CURRENT <stem>
  916. //*     CLEAR
  917. //*     TTXSAME "<file>" "<project>" "<macro>"
  918. //*     LOAD    "<error>" "<dir>" "<sourcefile>" <args>
  919. //*
  920. //*   Where <stem> Is any valid rexx variable name
  921. //*
  922. //***********************************************************************
  923.  
  924.  
  925. long
  926. DoRexxCommand(msg, port, arg0, pres)
  927. void *msg;              //  RexxMsg structure if we need it
  928. struct MsgPort *port;   //  MsgPort structure if we need it
  929. char *arg0;             //  arg0
  930. char **pres;            //  where to put our result if rc==0
  931. {
  932.     char cmd[9];
  933.     char *p;
  934.  
  935.     strncpy(cmd, arg0, 8);
  936.     cmd[8] = 0;         // Ensure that the string is properly terminated
  937.     p = strchr(cmd, ' ');
  938.     if (p) *p = 0;
  939.  
  940.     p = arg0+strlen(cmd);
  941.     while (*p == ' ') p++;
  942.  
  943.     //*******************************************************
  944.     //*  QUIT - Terminate the error parser                  *
  945.     //*******************************************************
  946.     if (!stricmp(cmd, "QUIT"))
  947.     {
  948.         run_arexx_server = 0;
  949.         return(0);
  950.     }
  951.  
  952.     //*******************************************************
  953.     //*  CLEAR - Clear out all errors stored                *
  954.     //*******************************************************
  955.     if (!stricmp(cmd, "CLEAR"))
  956.     {
  957.        while(files.next != &files)
  958.           FreeFile(files.next);
  959.        return(0);
  960.     }
  961.  
  962.     //*******************************************************
  963.     //*  TTXSAME - Invoke TTX on a file                     *
  964.     //*******************************************************
  965.     if (!stricmp(cmd, "TTXSAME"))
  966.     {
  967.        int ac;
  968.        char *hold;
  969.        char *av[3];
  970.        int rc = 0;
  971.  
  972.        av[0] = av[1] = av[2] = 0;
  973.        hold = strdup(p);
  974.  
  975.        if (hold == NULL)
  976.        {
  977.           return(10);    // No memory
  978.        }
  979.  
  980.        ac = _parseargs1(hold, strlen(hold));
  981.        if (ac)
  982.        {
  983.           if (ac > 3)
  984.              ac = 3;
  985.           _parseargs2(hold, av, ac);
  986.           av[0] = full_path(av[0]);
  987.           *pres = do_ttx(av[0], av[1], av[2]);
  988.           free(av[0]);
  989.           if (*pres == NULL)
  990.              rc = 5;
  991.        }
  992.        free(hold);
  993.        return(rc);
  994.     }
  995.  
  996.     //*******************************************************
  997.     //*  LOAD - Load errors into the parser                 *
  998.     //*******************************************************
  999.     if (!stricmp(cmd, "LOAD"))
  1000.     {
  1001.        int ac;
  1002.        char *hold;
  1003. #define LOAD_PARMS 4
  1004.        char *av[LOAD_PARMS];
  1005.        int rc = 0;
  1006.  
  1007.        av[0] = av[1] = av[2] = av[3] = 0;
  1008.        hold = strdup(p);
  1009.  
  1010.        if (hold == NULL)
  1011.        {
  1012.           return(10);    // No memory
  1013.        }
  1014.  
  1015.        ac = _parseargs1(hold, strlen(hold));
  1016.        if (ac)
  1017.        {
  1018.           if (ac > LOAD_PARMS)
  1019.              ac = LOAD_PARMS;
  1020.           _parseargs2(hold, av, ac);
  1021.           *pres = do_load(av[0], av[1], av[2], av[3]);
  1022.           if (*pres == NULL)
  1023.              rc = 5;
  1024.        }
  1025.        free(hold);
  1026.        return(rc);
  1027.     }
  1028.  
  1029.     //*******************************************************
  1030.     //*  NEXT - Advance to the next error                   *
  1031.     //*******************************************************
  1032.     if (!stricmp(cmd, "NEXT"))
  1033.     {
  1034.        do_next();
  1035.     }
  1036.     //*******************************************************
  1037.     //*  FIRST - Go to the first error                      *
  1038.     //*******************************************************
  1039.     else if (!stricmp(cmd, "FIRST"))
  1040.     {
  1041.        curfile = NULL;
  1042.        do_next();
  1043.     }
  1044.     //*******************************************************
  1045.     //*  PREV - Move to the previous error                  *
  1046.     //*******************************************************
  1047.     else if (!stricmp(cmd, "PREV"))
  1048.     {
  1049.        do_prev();
  1050.     }
  1051.     //*******************************************************
  1052.     //*  CURRENT - Return information on the current error  *
  1053.     //*******************************************************
  1054.     else if (stricmp(cmd, "CURRENT"))
  1055.     {
  1056.         // Unrecognized command, let them know about it
  1057.         *pres = "Command Unknown";
  1058.         return(5);
  1059.     }
  1060.     return(SetStem(msg, p));
  1061. }
  1062.  
  1063.  
  1064. //***********************************************************************
  1065. //* Procedure: main
  1066. //* Synopsis:  rc = main(argc, argv);
  1067. //* Purpose:   Main entry point
  1068. //***********************************************************************
  1069. int main(int argc, char **argv)
  1070. {
  1071.    struct SAVER {
  1072.       struct SAVER *next;
  1073.       char         *fname;
  1074.    };
  1075.  
  1076.    int i;
  1077.    char *macro;
  1078.    char *project;
  1079.    struct SAVER base, *nsaver;
  1080.  
  1081.    run_arexx_server  = 0;
  1082.    files.prev = &files;
  1083.    files.next = &files;
  1084.    curfile = NULL;
  1085.  
  1086.    macro   = NULL;
  1087.    project = NULL;
  1088.    nsaver = &base;
  1089.    base.next = NULL;
  1090.  
  1091.    //
  1092.    // DICE automatically opens rexxsyslib.library for us as long
  1093.    // as we reference the base variable (via extern) and not
  1094.    // declare it.  lib/rexx.h does this for us.
  1095.    //
  1096.    // However, unlike other autoinits, if DICE is unable to open
  1097.    // the library it does not abort the program, hence the following.
  1098.    //
  1099.    if (RexxSysBase == NULL)
  1100.    {
  1101.       say("Unable to open rexxsyslib.library !");
  1102.       exit(20);
  1103.    }
  1104.  
  1105.    if ((argc == 2) && (!strcmp(argv[1], "?"))) usage();
  1106.  
  1107.    // Template for command:
  1108.    //   FILE/M,MACRO/K,PROJECT/K,REXXSTARTUP/S" VERSTAG);
  1109.    for (i = 1; i < argc; i++)
  1110.    {
  1111.       if (!stricmp(argv[i], "MACRO"))
  1112.       {
  1113.          if (++i >= argc) usage();
  1114.          if (macro != NULL) usage();
  1115.          macro = malloc(strlen(argv[i]) + 16); // ExecArexxMacro
  1116.          if (macro)
  1117.          {
  1118.             strcpy(macro, "ExecArexxMacro ");
  1119.             strcat(macro, argv[i]);
  1120.          }
  1121.       }
  1122.       else if (!stricmp(argv[i], "PROJECT"))
  1123.       {
  1124.          if (++i >= argc) usage();
  1125.          project = argv[i];
  1126.       }
  1127.       else if (!stricmp(argv[i], "REXXSTARTUP"))
  1128.       {
  1129.          run_arexx_server = 1;
  1130.       }
  1131.       else
  1132.       {
  1133.          if (!stricmp(argv[i], "FILE"))
  1134.          {
  1135.             if (++i >= argc) usage();
  1136.          }
  1137.          nsaver->next = malloc(sizeof(struct SAVER));
  1138.          if (nsaver->next)
  1139.          {
  1140.             nsaver = nsaver->next;
  1141.             nsaver->fname = full_path(argv[i]);
  1142.             nsaver->next  = NULL;
  1143.          }
  1144.       }
  1145.    }
  1146.  
  1147.    if (run_arexx_server)
  1148.    {
  1149.       short r = CreateGlobalDiceRexxPort(NULL, PORTNAME);
  1150.       if (r < 0)
  1151.       {
  1152.          say("AREXX Port " PORTNAME " exists.  DError is already running\n");
  1153.          exit(20);
  1154.       }
  1155.    }
  1156.    else
  1157.    {
  1158.       CreateDiceRexxPort(NULL, NULL);
  1159.    }
  1160.  
  1161.    // We have parsed our parameters, now we need to load each of the files
  1162.    // specified into the editor.  In the process we will have to check for
  1163.    // Any files in the editor to ensure that they are not already loaded
  1164.    while((nsaver = base.next) != NULL)
  1165.    {
  1166.       char *portname;
  1167.  
  1168.       portname = do_ttx(nsaver->fname, project, macro);
  1169.       if (portname == NULL)
  1170.          say("Unable to edit file");
  1171.  
  1172.       // Take the entry off the list because we have already processed it
  1173.       base.next = nsaver->next;
  1174.       free(nsaver->fname);
  1175.       free(nsaver);
  1176.    }
  1177.  
  1178.    if (run_arexx_server)
  1179.    {
  1180.       //
  1181.       // Our main loop executes received commands
  1182.       //
  1183.       while (run_arexx_server)
  1184.       {
  1185.          long mask = Wait(SIGBREAKF_CTRL_C | (1 << RexxSigBit));
  1186.  
  1187.          if (mask & SIGBREAKF_CTRL_C)
  1188.             break;
  1189.  
  1190.          if (mask & (1 << RexxSigBit))
  1191.              ProcessRexxCommands(NULL);
  1192.       }
  1193.       say("*** DError terminating\n");
  1194.    }
  1195.    return(0);
  1196. }
  1197.